/**
 * \addtogroup uip6
 * @{
 */

/**
 * \file
 *         Neighbor discovery (RFC 4861)
 * \author Mathilde Durvy <mdurvy@cisco.com>
 * \author Julien Abeille <jabeille@cisco.com>
 */

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
/*
 * Copyright (c) 2006, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <string.h>
#include "net/uip-icmp6.h"
#include "nd-opt/uip-nd6.h"
#include "nd-opt/uip-ds6.h"
#include "lib/random.h"

/*------------------------------------------------------------------*/
#if UIP_CONF_ROUTER
//TODO: Implement 6LoWPAN-ND optimizations for 6LRs and/or 6LBRs
#error 6LoWPAN-ND optimizations not yet supported for routers.
#endif /* UIP_CONF_ROUTER */

#define DEBUG 0
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15])
#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5])
#else
#define PRINTF(...)
#define PRINT6ADDR(addr)
#define PRINTLLADDR(addr)
#endif

#if UIP_LOGGING
#include <stdio.h>
void uip_log(char *msg);

#define UIP_LOG(m) uip_log(m)
#else
#define UIP_LOG(m)
#endif /* UIP_LOGGING == 1 */

/*------------------------------------------------------------------*/
/** @{ */
/** \name Pointers to the header structures.
 *  All pointers except UIP_IP_BUF depend on uip_ext_len, which at
 *  packet reception, is the total length of the extension headers.
 *  
 *  The pointer to ND6 options header also depends on nd6_opt_offset,
 *  which we set in each function.
 *
 *  Care should be taken when manipulating these buffers about the
 *  value of these length variables
 */

#define UIP_IP_BUF                ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])  /**< Pointer to IP header */
#define UIP_ICMP_BUF            ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len])  /**< Pointer to ICMP header*/
/**@{  Pointers to messages just after icmp header */
#define UIP_ND6_RS_BUF            ((uip_nd6_rs *)&uip_buf[uip_l2_l3_icmp_hdr_len])
#define UIP_ND6_RA_BUF            ((uip_nd6_ra *)&uip_buf[uip_l2_l3_icmp_hdr_len])
#define UIP_ND6_NS_BUF            ((uip_nd6_ns *)&uip_buf[uip_l2_l3_icmp_hdr_len])
#define UIP_ND6_NA_BUF            ((uip_nd6_na *)&uip_buf[uip_l2_l3_icmp_hdr_len])
/** @} */
/** Pointer to ND option */
#define UIP_ND6_OPT_HDR_BUF  ((uip_nd6_opt_hdr *)&uip_buf[uip_l2_l3_icmp_hdr_len + nd6_opt_offset])
#define UIP_ND6_OPT_PREFIX_BUF ((uip_nd6_opt_prefix_info *)&uip_buf[uip_l2_l3_icmp_hdr_len + nd6_opt_offset])
#define UIP_ND6_OPT_MTU_BUF ((uip_nd6_opt_mtu *)&uip_buf[uip_l2_l3_icmp_hdr_len + nd6_opt_offset])
/** @} */

static u8_t nd6_opt_offset;                     /** Offset from the end of the icmpv6 header to the option in uip_buf*/
static u8_t *nd6_opt_llao;   /**  Pointer to llao option in uip_buf */
static uip_nd6_opt_aro *nd6_opt_aro;   /**  Pointer to aro option in uip_buf */
#if !UIP_CONF_ROUTER            // TBD see if we move it to ra_input
static uip_nd6_opt_prefix_info *nd6_opt_prefix_info; /**  Pointer to prefix information option in uip_buf */
static uip_ipaddr_t ipaddr;
static uip_ds6_prefix_t *prefix; /**  Pointer to a prefix list entry */
#endif /* !UIP_CONF_ROUTER */
#if CONF_6LOWPAN_ND_6CO
static uip_nd6_opt_6co *nd6_opt_6co; /**  Pointer to a 6lowpan context option in uip_buf */
static uip_ds6_addr_context_t *context; /**  Pointer to a Context Table entry */
#endif /* CONF_6LOWPAN_ND_6CO */
static uip_ds6_nbr_t *nbr; /**  Pointer to a nbr cache entry*/
static uip_ds6_defrt_t *defrt; /**  Pointer to a router list entry */
static uip_ds6_addr_t *addr; /**  Pointer to an interface address */
static uip_ds6_reg_t *reg; /**  Pointer to aregistration list entry */


/*------------------------------------------------------------------*/
/* create a llao */ 
static void
create_llao(u8_t *llao, u8_t type) {
  llao[UIP_ND6_OPT_TYPE_OFFSET] = type;
  llao[UIP_ND6_OPT_LEN_OFFSET] = UIP_ND6_OPT_LLAO_LEN >> 3;
  memcpy(&llao[UIP_ND6_OPT_DATA_OFFSET], &uip_lladdr, UIP_LLADDR_LEN);
  /* padding on some */
  memset(&llao[UIP_ND6_OPT_DATA_OFFSET + UIP_LLADDR_LEN], 0,
         UIP_ND6_OPT_LLAO_LEN - 2 - UIP_LLADDR_LEN);
}

/* create an aro */ 
static void
create_aro(u8_t* aro, u16_t lifetime) {
  ((uip_nd6_opt_aro*)aro)->type = UIP_ND6_OPT_ARO;
  ((uip_nd6_opt_aro*)aro)->len = UIP_ND6_OPT_ARO_LEN >> 3;
    ((uip_nd6_opt_aro*)aro)->status = (u8_t)0; /* Status: must be set to 0 in NS */
    ((uip_nd6_opt_aro*)aro)->lifetime = uip_htons(lifetime);
  memcpy(&(((uip_nd6_opt_aro*)aro)->eui64), &uip_lladdr, UIP_LLADDR_LEN);
}
/*------------------------------------------------------------------*/

void
uip_nd6_ns_input(void)
{

  PRINTF("Received NS from");
  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
  PRINTF("to");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF("with target address");
  PRINT6ADDR((uip_ipaddr_t *) (&UIP_ND6_NS_BUF->tgtipaddr));
  PRINTF("\n");
  UIP_STAT(++uip_stat.nd6.recv);

  u8_t flags;

#if UIP_CONF_IPV6_CHECKS
  if((UIP_IP_BUF->ttl != UIP_ND6_HOP_LIMIT) ||
     (uip_is_addr_mcast(&UIP_ND6_NS_BUF->tgtipaddr)) ||
     (UIP_ICMP_BUF->icode != 0)) {
    PRINTF("NS received is bad\n");
    goto discard;
  }
#endif /* UIP_CONF_IPV6_CHECKS */

  /* Options processing */
  nd6_opt_llao = NULL;
  nd6_opt_offset = UIP_ND6_NS_LEN;
  while(uip_l3_icmp_hdr_len + nd6_opt_offset < uip_len) {
#if UIP_CONF_IPV6_CHECKS
    if(UIP_ND6_OPT_HDR_BUF->len == 0) {
      PRINTF("NS received is bad\n");
      goto discard;
    }
#endif /* UIP_CONF_IPV6_CHECKS */
    switch (UIP_ND6_OPT_HDR_BUF->type) {
    case UIP_ND6_OPT_SLLAO:
      nd6_opt_llao = &uip_buf[uip_l2_l3_icmp_hdr_len + nd6_opt_offset];
#if UIP_CONF_IPV6_CHECKS
      /* There must be NO option in a DAD NS */
      if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) {
        PRINTF("NS received is bad\n");
        goto discard;
      } else {
#endif /*UIP_CONF_IPV6_CHECKS */
        nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
#if UIP_CONF_ROUTER      
        if(nbr == NULL) {
  /* Hosts only maintain NCEs for routers in 6lowpan-nd */
          uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr,
        (uip_lladdr_t *)&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
        0, NBR_STALE);
        } else {
#else
        if(nbr != NULL) {
#endif /* UIP_CONF_ROUTER */
          if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
        &nbr->lladdr, UIP_LLADDR_LEN) != 0) {
            memcpy(&nbr->lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
       UIP_LLADDR_LEN);
            nbr->state = NBR_STALE;
#if UIP_CONF_ROUTER
    /* Hosts can not have NCEs in INCOMPLETE state in 6lowpan-nd */
          } else {
            if(nbr->state == NBR_INCOMPLETE) {
              nbr->state = NBR_STALE;
            }
#endif /* UIP_CONF_ROUTER */
          }
        }
#if UIP_CONF_IPV6_CHECKS
      }
#endif /*UIP_CONF_IPV6_CHECKS */
      break;
    default:
      PRINTF("ND option not supported in NS");
      break;
    }
    nd6_opt_offset += (UIP_ND6_OPT_HDR_BUF->len << 3);
  }

  addr = uip_ds6_addr_lookup(&UIP_ND6_NS_BUF->tgtipaddr);
  if(addr != NULL) {
    if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) {
      goto discard;
    }
#if UIP_CONF_IPV6_CHECKS
    if(uip_ds6_is_my_addr(&UIP_IP_BUF->srcipaddr)) {
        /**
         * \NOTE do we do something here? we both are using the same address.
         * If we are doing dad, we could cancel it, though we should receive a
         * NA in response of DAD NS we sent, hence DAD will fail anyway. If we
         * were not doing DAD, it means there is a duplicate in the network!
         */
      PRINTF("NS received is bad\n");
      goto discard;
    }
#endif /*UIP_CONF_IPV6_CHECKS */
#if UIP_CONF_ROUTER
    /* Address resolution case. Hosts do neither perform nor respond to it */
    if(uip_is_addr_solicited_node(&UIP_IP_BUF->destipaddr)) {
      uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
      uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_ND6_NS_BUF->tgtipaddr);
      flags = UIP_ND6_NA_FLAG_SOLICITED | UIP_ND6_NA_FLAG_OVERRIDE;
      goto create_na;
    }
#endif /* UIP_CONF_ROUTER */
    /* NUD CASE */
    if(uip_ds6_addr_lookup(&UIP_IP_BUF->destipaddr) == addr) {
      uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr);
      uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_ND6_NS_BUF->tgtipaddr);
      flags = UIP_ND6_NA_FLAG_SOLICITED | UIP_ND6_NA_FLAG_OVERRIDE;
      goto create_na;
    } else {
#if UIP_CONF_IPV6_CHECKS
      PRINTF("NS received is bad\n");
      goto discard;
#endif /* UIP_CONF_IPV6_CHECKS */
    }
  } else {
    goto discard;
  }


create_na:
  uip_ext_len = 0;
  UIP_IP_BUF->vtc = 0x60;
  UIP_IP_BUF->tcflow = 0;
  UIP_IP_BUF->flow = 0;
  UIP_IP_BUF->len[0] = 0;       /* length will not be more than 255 */
  UIP_IP_BUF->len[1] = UIP_ICMPH_LEN + UIP_ND6_NA_LEN + UIP_ND6_OPT_LLAO_LEN;
  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
  UIP_IP_BUF->ttl = UIP_ND6_HOP_LIMIT;

  UIP_ICMP_BUF->type = ICMP6_NA;
  UIP_ICMP_BUF->icode = 0;

  UIP_ND6_NA_BUF->flagsreserved = flags;
  memcpy(&UIP_ND6_NA_BUF->tgtipaddr, &addr->ipaddr, sizeof(uip_ipaddr_t));

  create_llao(&uip_buf[uip_l2_l3_icmp_hdr_len + UIP_ND6_NA_LEN],
              UIP_ND6_OPT_TLLAO);

  UIP_ICMP_BUF->icmpchksum = 0;
  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();

  uip_len =
    UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ND6_NA_LEN + UIP_ND6_OPT_LLAO_LEN;

  UIP_STAT(++uip_stat.nd6.sent);
  PRINTF("Sending NA to");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF("from");
  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
  PRINTF("with target address");
  PRINT6ADDR(&UIP_ND6_NA_BUF->tgtipaddr);
  PRINTF("\n");
  return;

discard:
  uip_len = 0;
  return;
}


/*------------------------------------------------------------------*/
void
uip_nd6_ns_output(uip_ipaddr_t * src, uip_ipaddr_t * dest, uip_ipaddr_t * tgt,
                  u8_t aro, u16_t lifetime)
{
  uip_ext_len = 0;
  UIP_IP_BUF->vtc = 0x60;
  UIP_IP_BUF->tcflow = 0;
  UIP_IP_BUF->flow = 0;
  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
  UIP_IP_BUF->ttl = UIP_ND6_HOP_LIMIT;

  if(dest == NULL) {
    uip_create_solicited_node(tgt, &UIP_IP_BUF->destipaddr);
  } else {
    uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, dest);
  }
  UIP_ICMP_BUF->type = ICMP6_NS;
  UIP_ICMP_BUF->icode = 0;
  UIP_ND6_NS_BUF->reserved = 0;
  uip_ipaddr_copy((uip_ipaddr_t *) &UIP_ND6_NS_BUF->tgtipaddr, tgt);
  UIP_IP_BUF->len[0] = 0;       /* length will not be more than 255 */
  /*
   * check if we add a SLLAO option: for DAD, MUST NOT, for NUD, MAY
   * (here yes), for Address resolution , MUST 
   */
  if(src != NULL) {
    uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, src);
  } else {
    uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
  }
  create_llao(&uip_buf[uip_l2_l3_icmp_hdr_len + UIP_ND6_NS_LEN], UIP_ND6_OPT_SLLAO);
  if (aro) {
    create_aro(&uip_buf[uip_l2_l3_icmp_hdr_len + UIP_ND6_NS_LEN + UIP_ND6_OPT_LLAO_LEN],
                lifetime);
    uip_len =
          UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ND6_NS_LEN + UIP_ND6_OPT_LLAO_LEN + UIP_ND6_OPT_ARO_LEN;
  } else {
    uip_len =
          UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ND6_NS_LEN + UIP_ND6_OPT_LLAO_LEN;
  }
  UIP_IP_BUF->len[1] = uip_len - UIP_IPH_LEN;
  UIP_ICMP_BUF->icmpchksum = 0;
  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();

  UIP_STAT(++uip_stat.nd6.sent);
  PRINTF("Sending NS to");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF("from");
  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
  PRINTF("with target address");
  PRINT6ADDR(tgt);
  PRINTF("\n");
  return;
}



/*------------------------------------------------------------------*/
void
uip_nd6_na_input(void)
{
  PRINTF("Received NA from");
  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
  PRINTF("to");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF("with target address");
  PRINT6ADDR((uip_ipaddr_t *) (&UIP_ND6_NA_BUF->tgtipaddr));
  PRINTF("\n");
  UIP_STAT(++uip_stat.nd6.recv);

  /* 
   * booleans. the three last one are not 0 or 1 but 0 or 0x80, 0x40, 0x20
   * but it works. Be careful though, do not use tests such as is_router == 1 
   */
  u8_t is_llchange = 0;
  u8_t is_router = ((UIP_ND6_NA_BUF->flagsreserved & UIP_ND6_NA_FLAG_ROUTER));
  
  
  u8_t is_solicited =
    ((UIP_ND6_NA_BUF->flagsreserved & UIP_ND6_NA_FLAG_SOLICITED));
  u8_t is_override =
    ((UIP_ND6_NA_BUF->flagsreserved & UIP_ND6_NA_FLAG_OVERRIDE));

#if !UIP_CONF_ROUTER
/* We are not interested in NA messages not coming from a router */
  if (!is_router) {
    nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
    if (nbr != NULL) {
      uip_ds6_nbr_rm(nbr);
    }
    defrt = uip_ds6_defrt_lookup(&UIP_IP_BUF->srcipaddr);
    if (defrt != NULL) {
      uip_ds6_defrt_rm(defrt);
      /* Since we are deleting a default router, we must delete also all
       * registrations with that router. */
      uip_ds6_reg_cleanup_defrt(defrt);
      /* We will also need to start sending RS, as specified in I-D.ietf-6lowpan-nd
       * for a router that has become unreachable */
      uip_ds6_send_rs(NULL);
    }
    goto discard;
  }
#endif /* !UIP_CONF_ROUTER */

#if UIP_CONF_IPV6_CHECKS
  if((UIP_IP_BUF->ttl != UIP_ND6_HOP_LIMIT) ||
     (UIP_ICMP_BUF->icode != 0) ||
     (uip_is_addr_mcast(&UIP_ND6_NA_BUF->tgtipaddr)) ||
     (is_solicited && uip_is_addr_mcast(&UIP_IP_BUF->destipaddr))) {
    PRINTF("NA received is bad\n");
    goto discard;
  }
#endif /*UIP_CONF_IPV6_CHECKS */

  /* Options processing: we handle TLLAO and (if 6lowpan-nd is used) ARO, 
   * and must ignore others */
  nd6_opt_offset = UIP_ND6_NA_LEN;
  nd6_opt_llao = NULL;
  nd6_opt_aro = NULL;
  while(uip_l3_icmp_hdr_len + nd6_opt_offset < uip_len) {
#if UIP_CONF_IPV6_CHECKS
    if(UIP_ND6_OPT_HDR_BUF->len == 0) {
      PRINTF("NA received is bad\n");
      goto discard;
    }
#endif /*UIP_CONF_IPV6_CHECKS */
    switch (UIP_ND6_OPT_HDR_BUF->type) {
    case UIP_ND6_OPT_TLLAO:
      nd6_opt_llao = (u8_t *)UIP_ND6_OPT_HDR_BUF;
      break;
    case UIP_ND6_OPT_ARO:
      nd6_opt_aro = (uip_nd6_opt_aro *)UIP_ND6_OPT_HDR_BUF;
#if UIP_CONF_IPV6_CHECKS
      if((nd6_opt_aro->len != 2) ||
          (memcmp(&nd6_opt_aro->eui64, &uip_lladdr, UIP_LLADDR_LEN) != 0)) {
        /* ignore this option */
        nd6_opt_aro = NULL;
      }
#endif /* UIP_CONF_IPV6_CHECKS */
      break;
      default:
        PRINTF("ND option not supported in NA\n");
        break;
      }
      nd6_opt_offset += (UIP_ND6_OPT_HDR_BUF->len << 3);
    }
    addr = uip_ds6_addr_lookup(&UIP_ND6_NA_BUF->tgtipaddr);
    /* Message processing, including TLLAO and/or ARO if any */
    /* This block checks whether this na means dad failure. */
    if(addr != NULL) {
      goto discard;
    } else {
      nbr = uip_ds6_nbr_lookup(&UIP_ND6_NA_BUF->tgtipaddr);
      if(nbr == NULL) {
        goto discard;
      }
      if(nd6_opt_llao != 0) {
        is_llchange = memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], (void *)(&nbr->lladdr),
                        UIP_LLADDR_LEN);
      }
      if(nd6_opt_aro != NULL) {
        reg = uip_ds6_if.registration_in_progress;
        if (reg != NULL) {
          if ((nd6_opt_aro->lifetime == 0) && (reg->state == REG_TO_BE_UNREGISTERED)) {
            /* If the lifetime is 0, this means that the unregistration was successful;
             * we can delete the registration entry safely */
            uip_ds6_reg_rm(reg); /* Remove entry */
          } else {
            addr = uip_ds6_addr_lookup(&UIP_IP_BUF->destipaddr);
            switch(nd6_opt_aro->status) {
            case ARO_STATUS_SUCCESS:
              /* Make sure this is actually the address we are registerig */
              if (reg->addr == addr) {
                /* Clear the NS count */
                addr->state = ADDR_PREFERRED;
                reg->state = REG_REGISTERED;
                reg->reg_count = 0;
                stimer_set(&reg->reg_lifetime, uip_ntohs(nd6_opt_aro->lifetime) * 60);
                uip_ds6_if.registration_in_progress = NULL;
              }
              break;
            case ARO_STATUS_DUPLICATE:
              /* Remove the address */
              uip_ds6_addr_rm(addr);
              /* Clear registration_in_progress so that other registrations can occur */
              uip_ds6_if.registration_in_progress = NULL;
              /* If we have registered the address with any other router, we must void
               * such registrations by sending a ns with ARO with 0 lifetime */
              uip_ds6_reg_cleanup_addr(addr);
              break;
            case ARO_STATUS_RTR_NC_FULL:
               /* Remove entry. uip_ds6_periodic will try with other def. router
                * if possible */
              uip_ds6_reg_rm(reg);
              uip_ds6_if.registration_in_progress = NULL;
              break;
            default:
              break;
            }
          }
        }
      }
      if(!is_override && is_llchange) {
        if(nbr->state == NBR_REACHABLE) {
          nbr->state = NBR_STALE;
        }
        goto discard;
      } else {
        if(is_override || (!is_override && nd6_opt_llao != 0 && !is_llchange)
            || nd6_opt_llao == 0) {
          if(nd6_opt_llao != 0) {
            memcpy(&nbr->lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], UIP_LLADDR_LEN);
          }
          if(is_solicited) {
            nbr->state = NBR_REACHABLE;
            /* reachable time is stored in ms */
            stimer_set(&(nbr->reachable), uip_ds6_if.reachable_time / 1000);
          } else {
            if(nd6_opt_llao != 0 && is_llchange) {
              nbr->state = NBR_STALE;
            }
          }
        }
      }
    }

discard:
  uip_len = 0;
  return;
}


#if UIP_CONF_ROUTER
#if UIP_ND6_SEND_RA
/*---------------------------------------------------------------------------*/
void
uip_nd6_rs_input(void)
{

  PRINTF("Received RS from");
  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
  PRINTF("to");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF("\n");
  UIP_STAT(++uip_stat.nd6.recv);


#if UIP_CONF_IPV6_CHECKS
  /*
   * Check hop limit / icmp code 
   * target address must not be multicast
   * if the NA is solicited, dest must not be multicast
   */
  if((UIP_IP_BUF->ttl != UIP_ND6_HOP_LIMIT) || (UIP_ICMP_BUF->icode != 0)) {
    PRINTF("RS received is bad\n");
    goto discard;
  }
#endif /*UIP_CONF_IPV6_CHECKS */

  /* Only valid option is Source Link-Layer Address option any thing
     else is discarded */
  nd6_opt_offset = UIP_ND6_RS_LEN;
  nd6_opt_llao = NULL;

  while(uip_l3_icmp_hdr_len + nd6_opt_offset < uip_len) {
#if UIP_CONF_IPV6_CHECKS
    if(UIP_ND6_OPT_HDR_BUF->len == 0) {
      PRINTF("RS received is bad\n");
      goto discard;
    }
#endif /*UIP_CONF_IPV6_CHECKS */
    switch (UIP_ND6_OPT_HDR_BUF->type) {
    case UIP_ND6_OPT_SLLAO:
      nd6_opt_llao = UIP_ND6_OPT_HDR_BUF;
      break;
    default:
      PRINTF("ND option not supported in RS\n");
      break;
    }
    nd6_opt_offset += (UIP_ND6_OPT_HDR_BUF->len << 3);
  }
  /* Options processing: only SLLAO */
  if(nd6_opt_llao != NULL) {
#if UIP_CONF_IPV6_CHECKS
    if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) {
      PRINTF("RS received is bad\n");
      goto discard;
    } else {
#endif /*UIP_CONF_IPV6_CHECKS */
      if((nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr)) == NULL) {
        /* we need to add the neighbor */
        uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr,
                        &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET], 0, NBR_STALE);
      } else {
        /* If LL address changed, set neighbor state to stale */
        if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
      &nbr->lladdr, UIP_LLADDR_LEN) != 0) {
          memcpy(&nbr->lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
     UIP_LLADDR_LEN);
          nbr->state = NBR_STALE;
        }
        nbr->isrouter = 0;
      }
#if UIP_CONF_IPV6_CHECKS
    }
#endif /*UIP_CONF_IPV6_CHECKS */
  }

  /* Schedule a solicited RA */
  uip_ds6_send_ra_solicited();

discard:
  uip_len = 0;
  return;
}

/*---------------------------------------------------------------------------*/
void
uip_nd6_ra_output(uip_ipaddr_t * dest)
{

  UIP_IP_BUF->vtc = 0x60;
  UIP_IP_BUF->tcflow = 0;
  UIP_IP_BUF->flow = 0;
  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
  UIP_IP_BUF->ttl = UIP_ND6_HOP_LIMIT;

  if(dest == NULL) {
    uip_create_linklocal_allnodes_mcast(&UIP_IP_BUF->destipaddr);
  } else {
    /* For sollicited RA */
    uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, dest);
  }
  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);

  UIP_ICMP_BUF->type = ICMP6_RA;
  UIP_ICMP_BUF->icode = 0;

  UIP_ND6_RA_BUF->cur_ttl = uip_ds6_if.cur_hop_limit;

  UIP_ND6_RA_BUF->flags_reserved =
    (UIP_ND6_M_FLAG << 7) | (UIP_ND6_O_FLAG << 6);

  UIP_ND6_RA_BUF->router_lifetime = uip_htons(UIP_ND6_ROUTER_LIFETIME);
  //UIP_ND6_RA_BUF->reachable_time = uip_htonl(uip_ds6_if.reachable_time);
  //UIP_ND6_RA_BUF->retrans_timer = uip_htonl(uip_ds6_if.retrans_timer);
  UIP_ND6_RA_BUF->reachable_time = 0;
  UIP_ND6_RA_BUF->retrans_timer = 0;

  uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ND6_RA_LEN;
  nd6_opt_offset = UIP_ND6_RA_LEN;


  /* Prefix list */
  for(prefix = uip_ds6_prefix_list;
      prefix < uip_ds6_prefix_list + UIP_DS6_PREFIX_NB; prefix++) {
    if((prefix->isused) && (prefix->advertise)) {
      UIP_ND6_OPT_PREFIX_BUF->type = UIP_ND6_OPT_PREFIX_INFO;
      UIP_ND6_OPT_PREFIX_BUF->len = UIP_ND6_OPT_PREFIX_INFO_LEN / 8;
      UIP_ND6_OPT_PREFIX_BUF->preflen = prefix->length;
      UIP_ND6_OPT_PREFIX_BUF->flagsreserved1 = prefix->l_a_reserved;
      UIP_ND6_OPT_PREFIX_BUF->validlt = uip_htonl(prefix->vlifetime);
      UIP_ND6_OPT_PREFIX_BUF->preferredlt = uip_htonl(prefix->plifetime);
      UIP_ND6_OPT_PREFIX_BUF->reserved2 = 0;
      uip_ipaddr_copy(&(UIP_ND6_OPT_PREFIX_BUF->prefix), &(prefix->ipaddr));
      nd6_opt_offset += UIP_ND6_OPT_PREFIX_INFO_LEN;
      uip_len += UIP_ND6_OPT_PREFIX_INFO_LEN;
    }
  }

  /* Source link-layer option */
  create_llao((u8_t *)UIP_ND6_OPT_HDR_BUF, UIP_ND6_OPT_SLLAO);

  uip_len += UIP_ND6_OPT_LLAO_LEN;
  nd6_opt_offset += UIP_ND6_OPT_LLAO_LEN;

  /* MTU */
  UIP_ND6_OPT_MTU_BUF->type = UIP_ND6_OPT_MTU;
  UIP_ND6_OPT_MTU_BUF->len = UIP_ND6_OPT_MTU_LEN >> 3;
  UIP_ND6_OPT_MTU_BUF->reserved = 0;
  //UIP_ND6_OPT_MTU_BUF->mtu = uip_htonl(uip_ds6_if.link_mtu);
  UIP_ND6_OPT_MTU_BUF->mtu = uip_htonl(1500);

  uip_len += UIP_ND6_OPT_MTU_LEN;
  nd6_opt_offset += UIP_ND6_OPT_MTU_LEN;
  UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8);
  UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff);

  /*ICMP checksum */
  UIP_ICMP_BUF->icmpchksum = 0;
  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();

  UIP_STAT(++uip_stat.nd6.sent);
  PRINTF("Sending RA to");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF("from");
  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
  PRINTF("\n");
  return;
}
#endif /* UIP_ND6_SEND_RA */
#endif /* UIP_CONF_ROUTER */

#if !UIP_CONF_ROUTER
/*---------------------------------------------------------------------------*/
void
uip_nd6_rs_output(uip_ipaddr_t* rtr_ipaddr)
{
  UIP_IP_BUF->vtc = 0x60;
  UIP_IP_BUF->tcflow = 0;
  UIP_IP_BUF->flow = 0;
  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
  UIP_IP_BUF->ttl = UIP_ND6_HOP_LIMIT;
  if (rtr_ipaddr != NULL) {
    uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, rtr_ipaddr);
  } else {
    uip_create_linklocal_allrouters_mcast(&UIP_IP_BUF->destipaddr);
  } 
  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
  UIP_ICMP_BUF->type = ICMP6_RS;
  UIP_ICMP_BUF->icode = 0;
  UIP_IP_BUF->len[0] = 0;       /* length will not be more than 255 */

  if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) {
    UIP_IP_BUF->len[1] = UIP_ICMPH_LEN + UIP_ND6_RS_LEN;
    uip_len = uip_l3_icmp_hdr_len + UIP_ND6_RS_LEN;
  } else {
    uip_len = uip_l3_icmp_hdr_len + UIP_ND6_RS_LEN + UIP_ND6_OPT_LLAO_LEN;
    UIP_IP_BUF->len[1] =
      UIP_ICMPH_LEN + UIP_ND6_RS_LEN + UIP_ND6_OPT_LLAO_LEN;

    create_llao(&uip_buf[uip_l2_l3_icmp_hdr_len + UIP_ND6_RS_LEN],
    UIP_ND6_OPT_SLLAO);
  }

  UIP_ICMP_BUF->icmpchksum = 0;
  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();

  UIP_STAT(++uip_stat.nd6.sent);
  PRINTF("Sendin RS to");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF("from");
  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
  PRINTF("\n");
  return;
}


/*---------------------------------------------------------------------------*/
void
uip_nd6_ra_input(void)
{
  u8_t new_addr = 0;
  u8_t new_defrt = 0;
  nd6_opt_llao = NULL;
  nd6_opt_prefix_info = NULL;
  nd6_opt_6co = NULL;
  defrt = NULL;
  prefix = NULL;
  nbr = NULL;
  addr = NULL;
  context = NULL;

  PRINTF("Received RA from");
  PRINT6ADDR(&UIP_IP_BUF->srcipaddr);
  PRINTF("to");
  PRINT6ADDR(&UIP_IP_BUF->destipaddr);
  PRINTF("\n");
  UIP_STAT(++uip_stat.nd6.recv);

#if UIP_CONF_IPV6_CHECKS
  if((UIP_IP_BUF->ttl != UIP_ND6_HOP_LIMIT) ||
     (!uip_is_addr_link_local(&UIP_IP_BUF->srcipaddr)) ||
     (UIP_ICMP_BUF->icode != 0)) {
    PRINTF("RA received is bad");
    goto discard;
  }
#endif /*UIP_CONF_IPV6_CHECKS */

  if(UIP_ND6_RA_BUF->cur_ttl != 0) {
    uip_ds6_if.cur_hop_limit = UIP_ND6_RA_BUF->cur_ttl;
    PRINTF("uip_ds6_if.cur_hop_limit %u\n", uip_ds6_if.cur_hop_limit);
  }

  if(UIP_ND6_RA_BUF->reachable_time != 0) {
    if(uip_ds6_if.base_reachable_time !=
       uip_ntohl(UIP_ND6_RA_BUF->reachable_time)) {
      uip_ds6_if.base_reachable_time = uip_ntohl(UIP_ND6_RA_BUF->reachable_time);
      uip_ds6_if.reachable_time = uip_ds6_compute_reachable_time();
    }
  }
  if(UIP_ND6_RA_BUF->retrans_timer != 0) {
    uip_ds6_if.retrans_timer = uip_ntohl(UIP_ND6_RA_BUF->retrans_timer);
  }

  /* Options processing */
  nd6_opt_offset = UIP_ND6_RA_LEN;
  while(uip_l3_icmp_hdr_len + nd6_opt_offset < uip_len) {
    if(UIP_ND6_OPT_HDR_BUF->len == 0) {
      PRINTF("RA received is bad");
      goto discard;
    }
    switch (UIP_ND6_OPT_HDR_BUF->type) {
    case UIP_ND6_OPT_SLLAO:
      PRINTF("Processing SLLAO option in RA\n");
      nd6_opt_llao = (u8_t *) UIP_ND6_OPT_HDR_BUF;
      nbr = uip_ds6_nbr_lookup(&UIP_IP_BUF->srcipaddr);
      if(nbr == NULL) {
        nbr = uip_ds6_nbr_add(&UIP_IP_BUF->srcipaddr,
                              (uip_lladdr_t *)&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
                              1, NBR_STALE);
      } else {
#if UIP_CONF_ROUTER
  /* Hosts can not have NCEs in INCOMPLETE state in 6lowpan-nd */
        if(nbr->state == NBR_INCOMPLETE) {
          nbr->state = NBR_STALE;
        }
#endif /* UIP_CONF_ROUTER */        
        if(memcmp(&nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
            &nbr->lladdr, UIP_LLADDR_LEN) != 0) {
          memcpy(&nbr->lladdr, &nd6_opt_llao[UIP_ND6_OPT_DATA_OFFSET],
                  UIP_LLADDR_LEN);

          nbr->state = NBR_STALE;
        }
        nbr->isrouter = 1;
      }
      break;
    case UIP_ND6_OPT_MTU:
      PRINTF("Processing MTU option in RA\n");
      uip_ds6_if.link_mtu =
        uip_ntohl(((uip_nd6_opt_mtu *) UIP_ND6_OPT_HDR_BUF)->mtu);
      break;
    case UIP_ND6_OPT_PREFIX_INFO:
      PRINTF("Processing PREFIX option in RA\n");
      nd6_opt_prefix_info = (uip_nd6_opt_prefix_info *) UIP_ND6_OPT_HDR_BUF;
      if((uip_ntohl(nd6_opt_prefix_info->validlt) >= 
          uip_ntohl(nd6_opt_prefix_info->preferredlt)) &&
          (!uip_is_addr_link_local(&nd6_opt_prefix_info->prefix))) {
        /* on-link flag related processing */
        if(!(nd6_opt_prefix_info->flagsreserved1 & UIP_ND6_RA_FLAG_ONLINK)) {
      /* I.D.ietf-6lowpan-nd section 5.4: Should the host erroneously receive
       * a Prefix Information option with the 'L' (on-link) flag set, then that
       * Prefix Information Option (PIO) MUST be ignored. */
          prefix = uip_ds6_prefix_lookup(&nd6_opt_prefix_info->prefix,
                                         nd6_opt_prefix_info->preflen);
          if(prefix == NULL) {
            if(nd6_opt_prefix_info->validlt != 0) {
              if(nd6_opt_prefix_info->validlt != UIP_ND6_INFINITE_LIFETIME) {
                prefix = uip_ds6_prefix_add(&nd6_opt_prefix_info->prefix,
                                            nd6_opt_prefix_info->preflen,
                                            uip_ntohl(nd6_opt_prefix_info->
                                                  validlt));
              } else {
                prefix = uip_ds6_prefix_add(&nd6_opt_prefix_info->prefix,
                                            nd6_opt_prefix_info->preflen, 0);
              }
            }
          } else {
            switch (nd6_opt_prefix_info->validlt) {
            case 0:
              uip_ds6_prefix_rm(prefix);
              break;
            case UIP_ND6_INFINITE_LIFETIME:
              prefix->isinfinite = 1;
              break;
            default:
              PRINTF("Updating timer of prefix");
              PRINT6ADDR(&prefix->ipaddr);
              PRINTF("new value %lu\n", uip_ntohl(nd6_opt_prefix_info->validlt));
              stimer_set(&prefix->vlifetime,
                         uip_ntohl(nd6_opt_prefix_info->validlt));
              prefix->isinfinite = 0;
              break;
            }
          }
          /* End of on-link flag related processing */
          /* Autonomous flag related processing */
          if((nd6_opt_prefix_info->flagsreserved1 & UIP_ND6_RA_FLAG_AUTONOMOUS) &&
              (nd6_opt_prefix_info->validlt != 0) &&
              (nd6_opt_prefix_info->preflen == UIP_DEFAULT_PREFIX_LEN)) {

            uip_ipaddr_copy(&ipaddr, &nd6_opt_prefix_info->prefix);
            uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr);
            addr = uip_ds6_addr_lookup(&ipaddr);
            if((addr != NULL) && (addr->type == ADDR_AUTOCONF)) {
              if(nd6_opt_prefix_info->validlt != UIP_ND6_INFINITE_LIFETIME) {
                /* The processing below is defined in RFC4862 section 5.5.3 e */
                if((uip_ntohl(nd6_opt_prefix_info->validlt) > 2 * 60 * 60) ||
                   (uip_ntohl(nd6_opt_prefix_info->validlt) >
                    stimer_remaining(&addr->vlifetime))) {
                  PRINTF("Updating timer of address");
                  PRINT6ADDR(&addr->ipaddr);
                  PRINTF("new value %lu\n",
                         uip_ntohl(nd6_opt_prefix_info->validlt));
                  stimer_set(&addr->vlifetime,
                             uip_ntohl(nd6_opt_prefix_info->validlt));
                } else {
                  stimer_set(&addr->vlifetime, 2 * 60 * 60);
                  PRINTF("Updating timer of address ");
                  PRINT6ADDR(&addr->ipaddr);
                  PRINTF("new value %lu\n", (unsigned long)(2 * 60 * 60));
                }
                addr->isinfinite = 0;
              } else {
                addr->isinfinite = 1;
              }
            } else {
              /* We are configuring a new address! */
              if(uip_ntohl(nd6_opt_prefix_info->validlt) ==
                  UIP_ND6_INFINITE_LIFETIME) {
                addr = uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF);
              } else {
                addr = uip_ds6_addr_add(&ipaddr, uip_ntohl(nd6_opt_prefix_info->validlt),
                                 ADDR_AUTOCONF);
              }
              if (addr != NULL) {
                new_addr = 1;
              }
            }
          }
        }
        /* End of autonomous flag related processing */
      }
      break;
#if CONF_6LOWPAN_ND_6CO
  case UIP_ND6_OPT_6CO:
    nd6_opt_6co = (uip_nd6_opt_6co*)&uip_buf[uip_l2_l3_icmp_hdr_len + nd6_opt_offset];
    context = uip_ds6_context_lookup_by_id(nd6_opt_6co->res1_c_cid & UIP_ND6_RA_CID);
    if (context != NULL) {
      /* Context already exists. Update*/
      if (nd6_opt_6co->lifetime != 0) {
        context->length = nd6_opt_6co->preflen;
        uip_ipaddr_copy(&context->prefix, &nd6_opt_6co->prefix);
        if (nd6_opt_6co->res1_c_cid & UIP_ND6_RA_FLAG_COMPRESSION) {
          context->state = IN_USE_COMPRESS;
        } else {
          context->state = IN_USE_UNCOMPRESS_ONLY;
        }
        /* Lifetime field in 6CO expressed in units of 60 seconds */
        stimer_set(&context->vlifetime, uip_ntohs(nd6_opt_6co->lifetime) * 60);
        context->defrt_lifetime = uip_ntohs(UIP_ND6_RA_BUF->router_lifetime);
      } else {
        uip_ds6_context_rm(context);
      }
    } else {
      /* New context */
      if (nd6_opt_6co->lifetime != 0) {
        context = uip_ds6_context_add(nd6_opt_6co, uip_ntohs(UIP_ND6_RA_BUF->router_lifetime));
        if (context != NULL) {
          /* Lifetime field in 6CO expressed in units of 60 seconds */
          stimer_set(&context->vlifetime, uip_ntohs(nd6_opt_6co->lifetime) * 60);
        }
      }
    }
    break;
#endif /* CONF_6LOWPAN_ND_6CO */
#if CONF_6LOWPAN_ND_ABRO
  case UIP_ND6_OPT_ABRO:
    /* Process ABRO option */
    break
#endif /* CONF_6LOWPAN_ND_ABRO */
    default:
      PRINTF("ND option not supported in RA");
      break;
    }
    nd6_opt_offset += (UIP_ND6_OPT_HDR_BUF->len << 3);
  }

  defrt = uip_ds6_defrt_lookup(&UIP_IP_BUF->srcipaddr);
  if(UIP_ND6_RA_BUF->router_lifetime != 0) {
    if(nbr != NULL) {
      nbr->isrouter = 1;
    }
    if(defrt == NULL) {
      defrt = uip_ds6_defrt_add(&UIP_IP_BUF->srcipaddr,
                        (unsigned long)(uip_ntohs(UIP_ND6_RA_BUF->router_lifetime)));
      if (defrt != NULL) {
        new_defrt = 1;
      }
    } else {
      stimer_set(&(defrt->lifetime),
                 (unsigned long)(uip_ntohs(UIP_ND6_RA_BUF->router_lifetime)));
      if (defrt->sending_rs) {
        /* If we were sending unicast RSs to this particular router, stop doing it */
        defrt->sending_rs = 0;
        defrt->rscount = 0;
      }
    }
    /* In all cases, clear RS count */
    rscount = 0;
  } else { /* UIP_ND6_RA_BUF->router_lifetime == 0 */
    if(defrt != NULL) {
      uip_ds6_defrt_rm(defrt);
    }
  }

  /* Update the default router pointer in prefix, address, and context as
   * required */
  if ((defrt!= NULL) && (defrt->isused)) {
    if ((addr != NULL) && (addr->isused)) {
      addr->defrt = defrt;
    }
    if ((prefix != NULL) && (prefix->isused)) {
      prefix->defrt = defrt;
    }
    if ((context) && (context->state != NOT_IN_USE)) {
      context->defrt = defrt;
    }
  }

  if (new_addr) {
    /* If we have configured a new address, create the corresponding registration
     * entries in Garbage-collectible state */
    u8_t defrts = 0;
    for (defrt = uip_ds6_defrt_list;
        defrt < uip_ds6_defrt_list + UIP_DS6_DEFRT_NB; defrt++) {
      if (defrt->isused) {
        defrts++;
      }
    }
    defrts = defrts < UIP_DS6_REGS_PER_ADDR ? defrts : UIP_DS6_REGS_PER_ADDR;
    for(; defrts > 0; defrts--) {
      defrt = uip_ds6_defrt_choose_min_reg(addr);
      uip_ds6_reg_add(addr, defrt, REG_GARBAGE_COLLECTIBLE);
    }
  } else if (new_defrt) {
    /* We found a new router but we did not configure any new address from its
     * RA. This may happen for two different reasons:
     *
     * - The address lifetime was longer than the registration lifetime and
     * the router was down or out of range while this node tried to refresh the
     * registration of its address; in that case, the router has been deleted
     * from the default routers list but the address hasn't.
     *
     * - The RA does not contain a valid PIO. In this case, we do nothing */
    if (addr != NULL) {
      if (addr->type == ADDR_AUTOCONF) {
        uip_ds6_reg_add(addr, defrt, REG_GARBAGE_COLLECTIBLE);
      }
    }
  }
  
discard:
  uip_len = 0;
  return;
}
#endif /* !UIP_CONF_ROUTER */

 /** @} */
